
/**
  * 按照多态进行模式匹配，下面四个类继承ObjectMatch类
  */

abstract class ObjectMatch
case class Var(name: String) extends ObjectMatch
case class Number(num: Double) extends ObjectMatch
case class UnOp(operator: String, arg: ObjectMatch) extends ObjectMatch
case class BinOp(operator: String,
                 left: ObjectMatch, right: ObjectMatch) extends ObjectMatch
object ObjectMatchTest {

  def simplifyTop(expr: ObjectMatch): ObjectMatch = expr match {
    case UnOp("-", UnOp("-", e))  => e   // Double negation
    case BinOp("+", e, Number(0)) => e   // Adding zero
    case BinOp("*", e, Number(1)) => e   // Multiplying by one
    case _ => expr
  }

  def checkbinary(expr: ObjectMatch) {
    expr match {
      case BinOp(op, left, right) =>
        println(expr +" is a binary operation")
      case _ =>
    }
  }

  def checkbinary1(expr: ObjectMatch) {
    expr match {
      case BinOp(op, left, right) =>
        println(expr +"is a binary operation")
      case _ =>
    }
  }

  def checkbinary2(expr: ObjectMatch) {
    expr match {
      case BinOp(_, _, _) => println(expr +"is a binary operation")
      case _ => println("It's something else")
    }
  }

  def describe(e: ObjectMatch): String = e match {
    case Number(_) => "a number"
    case Var(_) => "a variable"
    case _ => throw new RuntimeException // Should not happen
  }

  def f() = 10

  def deepmatch(expr: ObjectMatch) = {
    expr match {
      case BinOp("+", e, Number(0)) => println("a deep match")
      case _ =>
    }
  }

  def startsWithZero(expr: Any) = {
    expr match {
      case 0 => "zero"
      case somethingElse => "not zero: "+ somethingElse
    }
  }

  def startsWithZero1(expr: List[Int]) =
    expr match {
      case List(0, _, _) => println("found it")
      case _ =>
    }

  def startsWithZero2(expr: List[Int]) =
    expr match {
      case List(0, _*) => println("found it")
      case _ =>
    }

  object OtherDescribe {
    def describe(e: ObjectMatch): String = (e: @unchecked) match {
      case Number(_) => "a number"
      case Var(_)    => "a variable"
    }
  }

  def isInstance(expr: Any) = {
    if (
      expr.isInstanceOf[String]
    ) {
      val s =
        expr.asInstanceOf[String]
      s.length
    } else 0
  }

  def matchUnOp(expr: ObjectMatch) = {
    expr match {
      case UnOp("abs", e @ UnOp("abs", _)) => e
      case _ =>
    }
  }

  def main(args: Array[String]) {
    val varObjectMatch = new Var("var")
    val zeroObjectMatch = new Number(0)
    val binopObjectMatch = new BinOp("=", varObjectMatch, varObjectMatch)
    val unopObjectMatch = new UnOp("abs", new Number(3))

    checkbinary(varObjectMatch)
    checkbinary(binopObjectMatch)

    checkbinary1(varObjectMatch)
    checkbinary1(binopObjectMatch)

    checkbinary2(varObjectMatch)
    checkbinary2(binopObjectMatch)

    println("describe(new Number(4)) [" + describe(new Number(4)) + "]")
    println("describe(varObjectMatch) [" + describe(varObjectMatch) + "]")
    println("OtherDescribe.describe(new Number(4)) [" +
      OtherDescribe.describe(new Number(4)) + "]")
    println("OtherDescribe.describe(varObjectMatch) [" +
      OtherDescribe.describe(varObjectMatch) + "]")
    println("f [" + f + "]")
    println("startsWithZero(0) [" + startsWithZero(0) + "]")
    println("startsWithZero(\"0\") [" + startsWithZero("0") + "]")
    deepmatch(zeroObjectMatch)
    deepmatch(new BinOp("+", varObjectMatch, zeroObjectMatch))
    startsWithZero1(List(0, 1, 2))
    startsWithZero1(List(0, 1))
    startsWithZero1(List(1, 0))
    startsWithZero2(List(0, 1, 2))
    startsWithZero2(List(0, 1))
    startsWithZero2(List(1, 0))
    println("isInstance(\"foo\") [" + isInstance("foo") + "]")
    println("matchUnOp(unopObjectMatch) [" + matchUnOp(unopObjectMatch) + "]")
    println("matchUnOp(new UnOp(\"abs\", unopObjectMatch)) [" + matchUnOp(new UnOp("abs", unopObjectMatch)) + "]")
  }

}
